package com.lagou.edu.factory;

import com.lagou.edu.factory.annotation.Autowired;
import com.lagou.edu.factory.annotation.Service;
import com.lagou.edu.factory.annotation.Transactional;
import com.lagou.edu.factory.scanner.ClassScanner;
import com.lagou.edu.factory.scanner.impl.PackageClassScanner;
import com.lagou.edu.factory.scanner.impl.TypeAnnotationMatcher;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class BeanFactory {


    private static Map<String, Object> map = new HashMap<>();  // 存储对象


    static {
        // 扫包  扫com.lagou.edu 下的所有类
        ClassScanner scanner = new PackageClassScanner();
        List<Class<?>> matchedClasses = scanner.scan("com.lagou.edu", new TypeAnnotationMatcher(Service.class));

        for (Class<?> clz : matchedClasses) {
            try {
                Object obj = clz.newInstance();
                map.put(getBeanName(clz), obj);
            } catch (InstantiationException | IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }

        Set<Map.Entry<String, Object>> entrySet = map.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            try {
                // 注入依赖
                injectDependencies(entry.getValue());

                // 配置事务代理
                enableTransactionProxy(entry.getKey(), entry.getValue());
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 判断给定的对象是否需要开启事务
     *
     * @param obj 给定的对象
     * @return 判断结果
     */
    private static boolean isTransactionEnable(Object obj) {
        Class<?> targetClass = obj.getClass();
        if (hasInterface(obj)) {
            Class<?>[] interfaces = targetClass.getInterfaces();
            targetClass = interfaces[0];
        }

        Method[] methods = targetClass.getDeclaredMethods();
        return Stream.of(methods).anyMatch(e -> e.getAnnotation(Transactional.class) != null);
    }

    /**
     * 判断给定的对象是否实现了接口
     *
     * @param obj 给定的对象
     * @return 判断结果
     */
    private static boolean hasInterface(Object obj) {
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        return (interfaces != null) && (interfaces.length != 0);
    }

    /**
     * 配置事务代理
     *
     * @param obj 要配置事务代理的对象
     */
    private static void enableTransactionProxy(String id, Object obj) {
        if (!isTransactionEnable(obj)) {
            return;
        }

        ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
        Object proxyObj = hasInterface(obj)
                ? proxyFactory.getJdkProxy(obj) : proxyFactory.getCglibProxy(obj);

        map.put(id, proxyObj);
    }

    /**
     * 实现对象属性的依赖注入
     *
     * @param obj 要注入依赖的对象
     */
    private static void injectDependencies(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        AccessibleObject.setAccessible(fields, true);

        List<Field> dependencies = Stream.of(fields)
                .filter(e -> e.isAnnotationPresent(Autowired.class)).collect(Collectors.toList());
        for (Field field : dependencies) {
            String requiredBeanId = getRequiredBeanId(field);
            field.set(obj, map.get(requiredBeanId));
        }
    }

    /**
     * 获取依赖的bean的ID
     *
     * @param field 要注入依赖的字段
     * @return 依赖的bean的ID
     */
    private static String getRequiredBeanId(Field field) {
        Class<?> clz = field.getType();
        String className = clz.getSimpleName();

        String firstCh = className.substring(0, 1);
        return String.format("%s%s", firstCh.toLowerCase(Locale.ROOT), className.substring(1));
    }

    /**
     * 获取bean的名称
     *
     * @param clz bean对应的类
     * @return bean的名称
     */
    private static String getBeanName(Class<?> clz) {
        Service service = clz.getAnnotation(Service.class);
        String beanName = service.value();
        if ((beanName != null) && !beanName.isEmpty()) {
            return beanName;
        }

        Class<?>[] interfaces = clz.getInterfaces();
        String className = clz.getSimpleName();
        if ((interfaces != null) && (interfaces.length != 0)) {
            className = interfaces[0].getSimpleName();
        }

        String firstCh = className.substring(0, 1);
        return String.format("%s%s", firstCh.toLowerCase(Locale.ROOT), className.substring(1));
    }


    // 对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }
}
